; *****************************************************************
; Fist stage bootloader for FAT12 - floppy
; Reindeer OS - boot loader
;
; Copyrights 2010 Michal Saman, m.saman@designplus.cz.
; This source code is release under the Apache License 2.0.
; http://www.apache.org/licenses/LICENSE-2.0
; *****************************************************************

%define PRG_CODE_SEGMENT			0x07C0

; read buffer for READ_SECTOR_EXT function
%define READ_BUFF_SEG				0x0900
%define	READ_BUFF_OFF				0x0000

org		0					; we set registers later
bits	16					; We are still in 16 bit Real Mode

start:		jmp  MAIN

nop
times 0x3B db 0

;*************************************************;
;	OEM Parameter block
;*************************************************;

bpbOEM					db "Reindeer"	
bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 1
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 	    DB 2
bpbRootEntries: 	    DW 224
bpbTotalSectors: 	    DW 2880
bpbMedia: 	            DB 0xF0
bpbSectorsPerFAT: 	    DW 9
bpbSectorsPerTrack: 	DW 18
bpbHeadsPerCylinder: 	DW 2
bpbHiddenSectors: 	    DD 0
bpbTotalSectorsBig:     DD 0
bsDriveNumber: 	        DB 0
bsUnused: 	            DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:	        DD 0xa0a1a2a3
bsVolumeLabel: 	        DB "MOS FLOPPY "
bsFileSystem: 	        DB "FAT12   "

sector					DB	0x00
head					DB	0x00
track					DB	0x00

datasector				dw	0x0000
cluster					dw	0x0000
imageName				db	"RDKRLD  SYS"

msgProgress	db		".", 0
msgLoading	db		"Reindeer OS loading", 0
msgCrLf     db		0x0D, 0x0A, 0x00
msgFailure	db		"ERR", 0

;*************************
; Print a string to screen
; DS:SI: 0 terminated string
;*************************

print:
	.WRITE:
		lodsb
		or		al, al
		jz		.PRINTDONE
		mov		ah, 0eh
		int		10h
		jmp		.WRITE	
	.PRINTDONE:
		ret	

;*****************************
; Covert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;*****************************

clusterToLba:
	sub		ax, 0x0002				; zero base cluster number
	xor		cx, cx
	mov		cl, BYTE [bpbSectorsPerCluster]
	mul		cx
	add		ax, WORD [datasector]
	ret

;*****************************
; Covert LBA to CHS function
; AX - LBA address to convert
; sector = (logical sector / sectors per track) + 1
; head = (logical sector / sectors per track) MOD number of heads
; track = logical sector / (sectors per track * number of heads)
;*****************************

lbaToChs:
	xor		dx, dx						; prepare dx:ax for operation
	div		WORD [bpbSectorsPerTrack]	;calculate
	inc		dl							;adjust for sector 0
	mov		BYTE [sector], dl
	xor		dx, dx
	div		WORD [bpbHeadsPerCylinder]
	mov		BYTE [head], dl
	mov		BYTE [track], al
	ret

;*************************
; Read Sector Function
; CX - number of sectors to read
; AX - starting sector
; ES:BX - buffer to read for
;*************************

readSectors:
	.MAIN:
		mov		di, 0x0005					; five retries for error
	.SECTORLOOP:
		push	ax
		push	bx
		push	cx
		call	lbaToChs
		mov		ah, 0x02					; function 02 of int 0x13
		mov		al, 0x01					; read 1 sector
		mov		dl, [bsDriveNumber]			; drive 0
		mov		ch, BYTE [track]			; cylinder number
		mov		cl, BYTE [sector]			; sector to read (The second sector)
		mov		dh, BYTE [head]				; head number
		int		0x13						; INT 13 - read sectors
		jnc		.OK
		xor		ax, ax						; function 01, reset disk
		int		0x13						; invoke bios
		dec		di							; decrement error counter
		pop		cx
		pop		bx
		pop		ax
		jnz		.SECTORLOOP
		int		0x18
	.OK:
		mov		si, msgProgress
		call	print
		pop		cx
		pop		bx
		pop		ax
		add		bx, WORD[bpbBytesPerSector]
		inc		ax
		loop	.MAIN
		ret

;***********************************
; Bootloader entry point
;***********************************

MAIN:
	cli								; disable interrupts
	mov		ax, PRG_CODE_SEGMENT	; setup register to point our segment
	mov		ds, ax
	mov		es, ax
	mov		fs, ax
	mov		gs, ax

	mov		ax, 0x0000
	mov		ss, ax
	mov		sp, 0xFFFF
	sti
	
	mov		si, msgLoading
	call	print

LOAD_ROOT:

	;compute size of root directory in sectors and store in CX
	xor		cx, cx
	xor		dx, dx
	mov		ax, 0x0020					; One entry in root table has 32 bytes
	mul		WORD [bpbRootEntries]		; Multiple count of entries to get total size in bytes
	div		WORD [bpbBytesPerSector]	; Get count of sector of root directory
	xchg	ax, cx

	; compute location of root directory and store in AX
	mov		al, BYTE [bpbNumberOfFATs]
	mul		WORD [bpbSectorsPerFAT]
	add		ax, WORD [bpbReservedSectors]	; in AX is now location of root directory
	mov		WORD [datasector], ax
	add		WORD [datasector], cx			; position of first data sector

	; read root directory to memory (07C0:0200)
	mov		bx, 0x0200						; position where we want to load root directory in memory
	call	readSectors

	;find rdkrldr.sys in root directory
	mov		cx, [bpbRootEntries]			;load loop counter
	mov		di, 0x200						; set the start of root directory in memory
	.LOOP:
		push	cx
		mov		cx, 0x000B					; eveleven character name
		mov		si, imageName				; rdkldr.sys
		push	di
	rep	cmpsb
		pop		di
		je		LOAD_FAT
		pop		cx
		add		di, 0x0020					; add 32 bytes - go to next entry
		loop	.LOOP
		jmp		FAILURE
	
	; Load FAT to memory at 0x200
	LOAD_FAT:
		; save first cluster address of our file
		mov		dx, WORD [di+0x001A]		; cluster position is on 26 byte in root directory entry
		mov		WORD [cluster], dx			; our file first cluster position (in LBA)
		; compute size of FAT and store in CX
		xor		ax, ax
		mov		al, BYTE [bpbNumberOfFATs]
		mul		WORD [bpbSectorsPerFAT]
		mov		cx, ax
		; compute location of FAT and store in AX
		mov		ax, WORD [bpbReservedSectors]
		; read FAT into memory (07C0:0200)
		mov		bx, 0x0200
		call	readSectors
		; set address for saving rdkldr.sys image to memory
		mov		ax, READ_BUFF_SEG
		mov		es, ax
		mov		bx, READ_BUFF_OFF
		push	bx

	;Load image of our file to memory
	LOAD_IMAGE:
		mov		ax, WORD [cluster]			; position of first cluster (LBA) of file
		pop		bx							; buffer to read into
		call	clusterToLba				; convert cluster to LBA
		xor		cx, cx
		mov		cl, BYTE [bpbSectorsPerCluster]		; sectors to read
		call	readSectors
		push	bx
		; compute next cluster
		mov		ax, WORD [cluster]
		mov		cx, ax						; copy current cluster
		mov		dx, ax						; copy current cluster
		shr		dx, 0x0001					; divide by two
		add		cx, dx						; sum for 3/2
		mov		bx, 0x200					; location of FAT
		add		bx, cx						; indext into FAT
		mov		dx, WORD [bx]				; read two bytes from FAT
		test	ax, 0x0001					; sudy nebo lichy?
		jnz		.ODD_CLUSTER
		.EVEN_CLUSTER:
			and		dx, 0000111111111111b	; take low twelve bits
			jmp		.DONE
		.ODD_CLUSTER:
			shr		dx, 0x0004				; take high twelve bits
		.DONE:
			mov		WORD [cluster], dx		; store position of new cluster
			cmp		dx, 0x0FF0				; test for end of file
			jb		LOAD_IMAGE

	DONE:
		mov		si, msgCrLf
		call	print
		xor		bx, bx						; set BX = 0; fylisystem type (0 - FAT12, 1 - iso9660)
		push	WORD READ_BUFF_SEG			; set stack to our address of file in memory
		push	WORD READ_BUFF_OFF
		retf								; goto memory - start RDKLDR.SYS	

	FAILURE:
		mov		si, msgFailure
		call	print
		mov		ah, 0x00
		int		0x16						; wait for keypress
		int		0x19						; war boot computer

times 510 - ($-$$) db 0				; We have to be 512 bytes, set rest of bytes with 0...
dw	0xAA55							; and to last two write boot signature.
